package com.dyzx.hbb.config;

import com.dyzx.hbb.security.context.SecurityContextHolder;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.jackson2.SimpleGrantedAuthorityMixin;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

// 配置适用于 JDK 17 和 Spring 6.* 版本
// 注意：Spring 6.0+ 已弃用 CachingConfigurerSupport，改为直接使用 @Configuration 和 @Bean 方法
// 参考：https://developer.aliyun.com/article/1310412


import java.time.Duration;
import java.util.Map;

@Configuration
@EnableCaching
public class RedisConfig {

    /**
     * 配置缓存管理器
     * 优化适配 Spring 6.* 和 JDK 17
     */
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 配置ObjectMapper解决类型安全
        ObjectMapper objectMapper = new ObjectMapper()
                .registerModule(new JavaTimeModule())
                .activateDefaultTyping(
                        LaissezFaireSubTypeValidator.instance,
                        ObjectMapper.DefaultTyping.NON_FINAL,
                        JsonTypeInfo.As.PROPERTY
                );

        // 主缓存配置（带类型信息的JSON序列化）
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(2))
                .prefixCacheNameWith(getPrefix())
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)))
                .disableCachingNullValues();

        // 特殊缓存配置示例（如纯字符串缓存）
        Map<String, RedisCacheConfiguration> configMap = Map.of(
                "stringCache", RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofMinutes(30))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair
                                .fromSerializer(new StringRedisSerializer()))
        );

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(defaultConfig)
                .withInitialCacheConfigurations(configMap)
                .enableStatistics()
                .transactionAware()
                .build();
    }
    /**
     * 获取缓存键的前缀
     * 优化适配 Spring 6.* 和 JDK 17
     */


    private String getPrefix() {
        String organId = getOrganIdFromToken();
        String moduleName = getModuleName(); // 获取模块名称
        String requestUrl = getRequestUrl(); // 获取请求路径
        return "erp" + organId + ":"  + requestUrl + ":";
    }

    private String getModuleName() {
        // 获取模块名称的逻辑，需要根据实际业务实现
        return "${moduleName}"; // 示例值，实际使用时替换为真实逻辑
    }

    private String getRequestUrl() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                return request.getRequestURI();
            }
        } catch (Exception e) {
            // 日志记录异常
            System.out.println("获取请求路径失败: " + e.getMessage());
        }
        return "default"; // 如果无法获取请求路径，返回默认值
    }

    private String getOrganIdFromToken() {
        Long organId = SecurityContextHolder.getOrganId();
        // 从Token中获取organId的逻辑，需要根据实际业务实现
        return String.valueOf(organId); // 示例值，实际使用时替换为真实逻辑
    }
    
    /**
     * 配置RedisTemplate
     * Spring 6.* 和 JDK 17 优化版本
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // Key序列化保持不变
        template.setKeySerializer(new StringRedisSerializer());

        // 使用新版构造器方式配置Jackson序列化
        ObjectMapper objectMapper = new ObjectMapper()
                .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
                .activateDefaultTyping(
                        LaissezFaireSubTypeValidator.instance,
                        ObjectMapper.DefaultTyping.NON_FINAL
                );

        Jackson2JsonRedisSerializer<Object> serializer =
                new Jackson2JsonRedisSerializer<>(objectMapper, Object.class); // 关键修改点

        template.setValueSerializer(serializer);
        return template;
    }
    @Bean
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(method.getName());
            for (Object param : params) {
                if (param != null) {
                    sb.append(":").append(param.toString());
                }
            }
            return sb.toString();
        };
    }
}